/*
 * (C) 2014-2015 Alibaba Group Holding Limited
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *
 *     __  ___   ______        ___       __          ___
 *    |  |/  /  /  __  \      /   \     |  |        /   \
 *    |  '  /  |  |  |  |    /  ^  \    |  |       /  ^  \
 *    |    <   |  |  |  |   /  /_\  \   |  |      /  /_\  \
 *    |  .  \  |  `--'  |  /  _____  \  |  `----./  _____  \
 *    |__|\__\  \______/  /__/     \__\ |_______/__/     \__\
 *
 *
 *
 */
//
// Created by KOALA TEAM on 12/19/14.
//

#import "KLModel.h"
#import "KLObjcUtils.h"
#import "KLLog.h"
#import <objc/runtime.h>


#import <objc/runtime.h>

#define _KLOBJECT_PROPERTY_KEY_MAP "_KLOBJECT_PROPERTY_KEY_MAP"


@interface _KLClassPropertyListMgr:NSObject{
    NSMutableDictionary *_propertyDict;
}

- (NSArray*)propertyList:(NSString*)className;
- (void)setPropertyList:(NSArray*)propertyList forClass:(NSString*)className;
@end

@implementation _KLClassPropertyListMgr


static _KLClassPropertyListMgr *SINGLETON = nil;

static bool isFirstAccess = YES;

#pragma mark - Public Method

+ (id)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        isFirstAccess = NO;
        SINGLETON = [[super allocWithZone:NULL] init];
    });
    
    return SINGLETON;
}

#pragma mark - Life Cycle

+ (id) allocWithZone:(NSZone *)zone
{
    return [self sharedInstance];
}

+ (id)copyWithZone:(struct _NSZone *)zone
{
    return [self sharedInstance];
}

+ (id)mutableCopyWithZone:(struct _NSZone *)zone
{
    return [self sharedInstance];
}

- (id)copy
{
    return [[_KLClassPropertyListMgr alloc] init];
}

- (id)mutableCopy
{
    return [[_KLClassPropertyListMgr alloc] init];
}

- (id) init
{
    if(SINGLETON){
        return SINGLETON;
    }
    if (isFirstAccess) {
        [self doesNotRecognizeSelector:_cmd];
    }
    if(self = [super init]){
        _propertyDict = [NSMutableDictionary new];
    }
    
    return self;
}

- (NSArray*)propertyList:(NSString*)className{
    return _propertyDict[className];
}
- (void)setPropertyList:(NSArray*)propertyList forClass:(NSString*)className{
    _propertyDict[className] = propertyList;
}
@end




@implementation KLModel

- (NSArray *)properties{
    return [self propertyList:NSStringFromClass(self.class)];
}


- (NSArray*)propertyList:(NSString *)className{
    NSArray* propertyArray = [[_KLClassPropertyListMgr sharedInstance]propertyList: className];
    if(!propertyArray){
        propertyArray = getPropertyNameListEx(NSClassFromString(className),[KLModel class]);
        [[_KLClassPropertyListMgr sharedInstance]setPropertyList:propertyArray forClass:className];
    }
    return propertyArray;
}

- (void)setObject:(id)object forKeyedSubscript:(NSString*)aKey{
    [self setValue:object forKey:aKey];
}

- (id)objectForKeyedSubscript:(NSString*)key{
    return [self valueForKey:key];
}

- (instancetype)copyWithZone:(NSZone *)zone{
    return [self clone];
}

- (NSData*) archivedData{
    return [NSKeyedArchiver archivedDataWithRootObject:self];
}
+ (id)unarchiveObject:(NSData*)data{
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}


+(BOOL)propertyIsOptional:(NSString*)propertyName{
    return YES;
}
+ (instancetype)cloneWithObject:(KLModel*)object{
    return [object clone];
}

+ (id)objectWithJson:(id)jsonObj error:(NSError**)err{
    if([jsonObj isKindOfClass:[NSArray class]])
        return [self arrayOfModelsFromDictionaries:jsonObj error:err];
    else 
        return [[self alloc]initWithDictionary:jsonObj error:err];

}
- (instancetype)clone{
    NSDictionary* dict = [self toDictionary];
    NSError *error = nil;
    id  object = nil;
    object = [[[self class] alloc] initWithDictionary:dict error:&error];
    NSAssert(object,@"clone failed:%@",error);
    return object;
}

- (void)copyFrom:(KLModel*)object{
    NSArray * properyList = [self properties];
    [properyList enumerateObjectsUsingBlock:^(NSString *propertyName, NSUInteger idx, BOOL *stop) {
        @try {
//            为了简单一点，就直接用mutableCopy了
            id value = object[propertyName];
            if([value conformsToProtocol:@protocol(NSMutableCopying)]){
                self[propertyName] = [value mutableCopy];
            }else{
                self[propertyName] = value;
            }
        }
        @catch (NSException *exception) {
            KLLogError(@"copyFrom:%@",exception);
        }

    }];
}

- (NSError*)copyFromDictionary:(NSDictionary *)dict{
    NSError *error = nil;
    id  object = nil;
    object = [[[self class] alloc] initWithDictionary:dict error:&error];
    NSAssert(object,@"clone failed:%@",error);
    [self copyFrom:object];
    return error;
}
@end
